package com.pi.shisan.service.audio;

import com.pi.shisan.service.util.SingleThreadCreator;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

import java.util.Comparator;
import java.util.NoSuchElementException;
import java.util.TreeSet;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 数字越大，等级越低
 * 最低从1开始 >= 1
 * 当高等级正在播放时，低等级只能挂起等待高等级播放完毕
 * 当低等级正在播放时，高等级抢占资源会挂起低等级，播放高等级播放器，完毕后重新恢复低等级
 * 同等级竞争时，会终止正在播放的，而后播放当前的。
 */
@Slf4j
public class AudioLevelManager {

    /**
     * 当前正在播放的等级
     * 0 - 初始化状态无正在播放的
     * 1 - 有任务正在播放
     */
    private AtomicInteger currentStatus = new AtomicInteger(0);

    /**
     * 给外部使用的非精准的正在播放的播放器对象
     * 可以使用该对象来完成中途对播放器的终止操作
     */
    private AudioMixerEntity outerStateMixer;

    /**
     * level值越低越靠前
     */
    private final TreeSet<AudioMixerEntity> mixerEntities = new NoExceptionTree<>(Comparator.comparing(AudioMixerEntity::getLevel));

    public void shutdown() {
        SingleThreadCreator.getAudioManager().exit();
        synchronized (mixerEntities) {
            for (AudioMixerEntity audioMixerEntity : mixerEntities) {
                audioMixerEntity.getAudioMixer().destroy();
            }
            mixerEntities.clear();
        }
    }

    public AudioMixerEntity getCurrentMixPlayer() {
        if (outerStateMixer != null) {
            AudioMixer audioMixer = outerStateMixer.getAudioMixer();
            if (audioMixer instanceof AudioPlayer && ((AudioPlayer)audioMixer).isDestroy()) {
                return null;
            }
        }
        return outerStateMixer;
    }

    public void playMixer(AudioMixer mixer, int level) {
        if (SingleThreadCreator.getAudioManager().isShutdown()) {
            return;
        }
        if (level <= 0) {
            throw new RuntimeException("level不支持");
        }
        if (this.currentStatus.compareAndSet(0, 1)) {
            AudioMixerEntity currentMixer = new AudioMixerEntity(level, mixer);
            synchronized (mixerEntities) {
                this.mixerEntities.add(currentMixer);
            }
            SingleThreadCreator.getAudioManager().execute(new PlayTask(this, currentMixer));
            return;
        }
        compareAndAdd(new AudioMixerEntity(level, mixer));
        if (currentStatus.compareAndSet(0,1)) {
            AudioMixerEntity audioMixerEntity;
            synchronized (mixerEntities) {
                audioMixerEntity = mixerEntities.first();
            }
            if (audioMixerEntity != null) {
                SingleThreadCreator.getAudioManager().execute(new PlayTask(this, audioMixerEntity));
            }
        }
    }

    /**
     * 返回被删除的
     * @param entity
     * @return
     */
    private void compareAndAdd(AudioMixerEntity entity) {
        AudioMixerEntity same;
        AudioMixerEntity lower = null;
        synchronized (mixerEntities) {
            same = this.mixerEntities.ceiling(entity);
            if (same != null) {
                if (same.getLevel() == entity.getLevel()) {
                    this.mixerEntities.remove(same);
                } else {
                    if (same.getLevel() > entity.getLevel()) {
                        lower = same;
                    }
                    same = null;
                }
            } else {
                lower = this.mixerEntities.higher(entity);
            }
            this.mixerEntities.add(entity);
        }
        if (same != null) {
            same.destroy();
        }
        if (lower != null) {
            lower.interrupt();
        }
    }

    public static class NoExceptionTree<T> extends TreeSet<T> {

        public NoExceptionTree(Comparator<? super T> comparator) {
            super(comparator);
        }

        @Override
        public T first() {
            try {
                return super.first();
            } catch (NoSuchElementException e) {
                return null;
            }
        }
    }

    public class PlayTask implements Runnable{

        private final AudioLevelManager audioLevelManager;
        private final AudioMixerEntity audioMixerEntity;

        public PlayTask(AudioLevelManager audioLevelManager, AudioMixerEntity audioMixerEntity) {
            this.audioLevelManager = audioLevelManager;
            this.audioMixerEntity = audioMixerEntity;
        }

        @Override
        public void run() {
            outerStateMixer = audioMixerEntity;
            if (audioMixerEntity.tryToPlay()) {
                synchronized (audioLevelManager.mixerEntities) {
                    AudioMixerEntity ceiling = audioLevelManager.mixerEntities.ceiling(audioMixerEntity);
                    if (audioMixerEntity == ceiling) {
                        audioLevelManager.mixerEntities.remove(audioMixerEntity);
                    }
                }
            } else {
                audioMixerEntity.resume();
            }
            AudioMixerEntity audioMixerEntity = null;
            synchronized (audioLevelManager.mixerEntities) {
                audioMixerEntity = audioLevelManager.mixerEntities.first();
            }
            if (audioMixerEntity != null) {
                SingleThreadCreator.getAudioManager().execute(new PlayTask(audioLevelManager, audioMixerEntity));
                return;
            }
            currentStatus.set(0);
            synchronized (audioLevelManager.mixerEntities) {
                audioMixerEntity = audioLevelManager.mixerEntities.first();
            }
            if (audioMixerEntity != null && currentStatus.compareAndSet(0,1)) {
                SingleThreadCreator.getAudioManager().execute(new PlayTask(audioLevelManager, audioMixerEntity));
            }
        }
    }

    @Setter
    @Getter
    public static class AudioMixerEntity {
        private int level;
        private AudioMixer audioMixer;

        /**
         * 0 -> 1才会播放
         */
        private final AtomicInteger status = new AtomicInteger(0);
        private final AtomicInteger checkHasInterrupt = new AtomicInteger(0);

        public AudioMixerEntity(int level, AudioMixer audioMixer) {
            this.level = level;
            this.audioMixer = audioMixer;
        }


        /**
         * 尝试播放
         * 0 - 未能成功播放
         * 1 - 播放完毕
         * 2 - 中断
         * @return
         */
        public boolean tryToPlay() {
            if (status.compareAndSet(0, 1)) {
                boolean finished = true;
                try {
                    finished = this.audioMixer.playOrResume();
                } catch (Exception e) {
                    log.error("尝试播放音乐失败:",e);
                }
                if (finished) {
                    status.set(3);
                } else {
                    status.set(2);
                    checkHasInterrupt.set(1);
                }
                synchronized (checkHasInterrupt) {
                    checkHasInterrupt.notifyAll();
                }
                return finished;
            }
            return false;
        }

        public void interrupt() {
            if (status.compareAndSet(0,2)) {
                this.audioMixer.interrupt();
                return;
            }
            while (!status.compareAndSet(1,2)) {
                if (status.get() >= 2) {
                    return;
                }
            }
            synchronized (checkHasInterrupt) {
                while (!checkHasInterrupt.compareAndSet(1,0) && status.get() == 2) {
                    this.audioMixer.interrupt();
                    try {
                        checkHasInterrupt.wait(1000);
                    } catch (InterruptedException e) {
                    }
                }
            }
        }

        public void destroy() {
            status.set(3);
            this.audioMixer.destroy();
        }

        public void resume() {
            status.compareAndSet(2,0);
        }
    }

    public interface AudioMixer{

        /**
         * 播放或恢复播放
         * 播放中不可返回，播放完毕后返回
         * 中断或销毁也需要返回
         * 如果播放完毕或被销毁返回true
         * 如果中断返回false
         * 此方法同一时刻仅能一个线程调用
         */
        boolean playOrResume();

        /**
         * 中断
         * 中断完成前不可返回
         * 可能在play之前被调用
         * 可能会和destroy并发调用
         * 在调用了interrupt后如果playOrResume无法结束将会一直调用
         */
        void interrupt();

        /**
         * 销毁
         * 销毁完成之前不可返回
         * 可能会在播放前被调用
         * 可能会和interrupt并发调用
         */
        void destroy();

    }

}
